package com.pekall.market.download;

import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.pekall.market.activity.DownloadPresenterImpl;
import com.pekall.market.application.MarketClassTwoApplication;
import com.pekall.market.bean.AppListInfo;
import com.pekall.market.config.configuration;
import com.pekall.market.db.Dao.AppInfoDao;
import com.pekall.market.db.Dao.SilenceInstallQueueDao;
import com.pekall.market.message.MdmSilenceInstallMessage;
import com.pekall.market.message.NotifyDataSetChangedMessage;
import com.pekall.market.rx.RxBus;
import com.pekall.market.util.ApplicationUtil;
import com.pekall.market.util.FileUtil;
import com.pekall.market.util.L;
import com.pekall.market.util.MD5Util;
import com.pekall.market.verify.IVerifyApkSignature;
import com.pekall.market.verify.JitVerifyUtil;
import com.pekall.pk.core.PkManager;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

import static com.pekall.market.application.MarketClassTwoApplication.getUnsafaOkHttpClient;
import static com.pekall.market.message.MdmSilenceInstallMessage.MessageType.install;
import static com.pekall.market.message.NotifyDataSetChangedMessage.Action.notifyDataSetChanged;

/**
 * http下载处理类
 * 
 */
public class HttpDownManager {
    /*记录下载数据*/
    public HashMap<String, DownInfo> downInfos;
    /*回调sub队列*/
    private HashMap<String, ProgressDownSubscriber> subMap;
    /*单例对象*/
    private volatile static HttpDownManager INSTANCE;

    private HttpDownManager() {
        downInfos = new HashMap<>();
        subMap = new HashMap<>();
        registerRxbusListener();
    }


    /**
     * 获取单例
     *
     * @return
     */
    public static HttpDownManager getInstance() {
        if (INSTANCE == null) {
            synchronized (HttpDownManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new HttpDownManager();
                }
            }
        }
        return INSTANCE;
    }


    /**
     * 开始下载
     */
    public void startDown(final DownInfo info) {
        if (info == null) {
            return;
        }

        if (!(downInfos.size() < 2)) {//当前已经有两个任务，则不能开启新任务
            Toast.makeText(MarketClassTwoApplication.getContext(), "最多同时下载两个应用.", Toast.LENGTH_SHORT).show();
            return;
        }
        /*正在下载的，则不再次启动下载了*/
        if (isDownloading(info)) {
            return;
        }

//        info.setUrl("http://www.izaodao.com/app/izaodao_app.apk");
        /*添加回调处理类*/
        ProgressDownSubscriber subscriber = new ProgressDownSubscriber(info);
        /*记录回调sub*/
        subMap.put(((AppListInfo.ContentBean) info).getAppId(), subscriber);
        /*获取service，多次请求公用一个sercie*/
        HttpService httpService;
        if (downInfos.containsKey(((AppListInfo.ContentBean) info).getAppId())) {
            httpService = info.getService();
        } else {
            downInfos.put(((AppListInfo.ContentBean) info).getAppId(), info);
            DownloadInterceptor interceptor = new DownloadInterceptor(subscriber, (AppListInfo.ContentBean) info);
            OkHttpClient.Builder builder = getUnsafaOkHttpClient();
            //手动创建一个OkHttpClient并设置超时时间
            builder.connectTimeout(info.getConnectionTime(), TimeUnit.SECONDS);
            builder.addInterceptor(interceptor);
            Retrofit retrofit = new Retrofit.Builder()
                    .client(builder.build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .baseUrl(configuration.getServerId())
                    .build();
            httpService = retrofit.create(HttpService.class);
            info.setService(httpService);
        }
        /*得到rx对象-上一次下載的位置開始下載*/
        httpService.download(String.valueOf(info.getReadLength()), info.getUrl())
                /*指定线程*/
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                   /*失败后的retry配置*/
                .retryWhen(new RetryWhenNetworkException())
                /*读取下载写入文件*/
                .map(new Func1<ResponseBody, DownInfo>() {
                    @Override
                    public DownInfo call(ResponseBody responseBody) {
                        try {
                            writeCache(responseBody, new File(info.getSavePath()), info);
                        } catch (IOException e) {
                            /*失败抛出异常*/
                            throw new HttpTimeException(e.getMessage());
                        }
                        return info;
                    }
                })
                /*回调线程*/
                .observeOn(AndroidSchedulers.mainThread())
                /*数据回调*/
                .subscribe(subscriber);

    }


    /**
     * 出错停止下载
     *
     * @param info
     */
    public void stopDownOnError(DownInfo info) {
        if (info == null) return;
        if (subMap.containsKey(((AppListInfo.ContentBean) info).getAppId())) {
            ProgressDownSubscriber subscriber = subMap.get(((AppListInfo.ContentBean) info).getAppId());
            //取消订阅
            subscriber.unsubscribe();
            //删除任务
            subMap.remove(((AppListInfo.ContentBean) info).getAppId());
            downInfos.remove(((AppListInfo.ContentBean) info).getAppId());
            Log.e("yh------------", "stopDownOnError:" + ((AppListInfo.ContentBean) info).getAppName() + "暂停下载,位置：" + info.getReadLength());
        }

        //保存应用信息
        AppInfoDao.getInstance().createOrUpdate((AppListInfo.ContentBean) info);

    }


    /**
     * 下载完成
     *
     * @param info
     */
    public void downCompleted(final DownInfo info) {
        if (info == null) return;
        if (subMap.containsKey(((AppListInfo.ContentBean) info).getAppId())) {
            ProgressDownSubscriber subscriber = subMap.get(((AppListInfo.ContentBean) info).getAppId());
            //注销订阅
            subscriber.unsubscribe();
            //删除任务
            subMap.remove(((AppListInfo.ContentBean) info).getAppId());
            downInfos.remove(((AppListInfo.ContentBean) info).getAppId());
            Log.e("yh------------", "downCompleted:" + ((AppListInfo.ContentBean) info).getAppName() + "下载完成,位置：" + info.getReadLength());

        }
        //下载完成后更新本地数据库信息
        AppInfoDao.getInstance().createOrUpdate((AppListInfo.ContentBean) info);
        //下载完成后上报信息
        DownloadPresenterImpl.getInstance(MarketClassTwoApplication.getContext()).uploadDownloadInfo((AppListInfo.ContentBean) info);
        if (!checkMd5((AppListInfo.ContentBean) info)) {
            L.w("dayan", "包不完整");

            //删除静默安装任务队列中的任务
            SilenceInstallQueueDao.getInstance().deleteBean(SilenceInstallQueueDao.getInstance().quryByField("appId", ((AppListInfo.ContentBean) info).getAppId()));
            //包不完整，则删除包
            FileUtil.delFile(info.getSavePath());
            Toast.makeText(MarketClassTwoApplication.getContext(), "应用包不完整", Toast.LENGTH_SHORT).show();
            return;
        }

        //验签操作。
        IVerifyApkSignature util = new JitVerifyUtil();


        if (util.isVerifyApkSignatureSuccess(info)) {//验签成功
            RxBus.getDefault().post(new MdmSilenceInstallMessage(install, (AppListInfo.ContentBean) info));
        } else {//验签失败，重置静默安装流程标识为结束,并且删除当前执行的静默安装的任务
            SilenceInstallQueueDao.getInstance().deleteBean(SilenceInstallQueueDao.getInstance().quryByField("appId", ((AppListInfo.ContentBean) info).getAppId()));
            Toast.makeText(MarketClassTwoApplication.getContext(), "应用签名验证失败，请确保从合法渠道获取应用", Toast.LENGTH_SHORT).show();
        }

    }

    /**
     * 根据md5判断包的完整性
     *
     * @param bean
     * @return
     */
    public static boolean checkMd5(AppListInfo.ContentBean bean) {
        if (bean != null
                && bean.getSavePath() != null
                && TextUtils.equals(bean.getMd5(), MD5Util.getFileMD5(new File(bean.getSavePath())))) {
            return true;
        }
        return false;
    }

    /**
     * 删除正在下载的任务
     *
     * @param info
     */
    public void deleteDown(DownInfo info) {
        if (info == null) return;

        if (subMap.containsKey(((AppListInfo.ContentBean) info).getAppId())) {
            ProgressDownSubscriber subscriber = subMap.get(((AppListInfo.ContentBean) info).getAppId());
            subscriber.unsubscribe();//取消下载订阅
            subMap.remove(((AppListInfo.ContentBean) info).getAppId());
            downInfos.remove(((AppListInfo.ContentBean) info).getAppId());
            info.setCountLength(0);
            info.setReadLength(0);
            info.setState(DownState.STOP);
            info.setUrl(null);
            info.setSavePath(null);
            AppInfoDao.getInstance().createOrUpdate((AppListInfo.ContentBean) info);
        }
        /*同步数据库*/
    }

    /**
     * 注册RXbus监听器
     */
    public void registerRxbusListener() {
        RxBus.getDefault().toObservable(NotifyDataSetChangedMessage.class)
                //在io线程进行订阅，可以执行一些耗时操作
                .subscribeOn(Schedulers.io())
                //在主线程进行观察，可做UI更新操作
                .observeOn(AndroidSchedulers.mainThread())
                //观察的对象
                .subscribe(new Action1<NotifyDataSetChangedMessage>() {
                    @Override
                    public void call(NotifyDataSetChangedMessage message) {
//                        ServiceAppListDataPresenterImpl.getInstance().getDataFromLocal(mAppType, ParentFragment.this);//从网络获取数据
                        switch (message.action) {
                            case deleteDownload:
                                deleteAllDown();//删除所有任务后，通知刷新ui
                                break;
                            default:
                                break;
                        }
                    }
                });
    }

    /**
     * 暂停下载
     *
     * @param info
     */
    public void pause(DownInfo info) {
        if (info == null) return;

        if (subMap.containsKey(((AppListInfo.ContentBean) info).getAppId())) {
            ProgressDownSubscriber subscriber = subMap.get(((AppListInfo.ContentBean) info).getAppId());
            subscriber.unsubscribe();
            Log.e("yh------------", ((AppListInfo.ContentBean) info).getAppName() + "已暂停! 文件位置:" + info.getReadLength());
            subMap.remove(((AppListInfo.ContentBean) info).getAppId());
            downInfos.remove(((AppListInfo.ContentBean) info).getAppId());

            //如果应用正在下载，则将状态设置为暂停。
            info.setState(DownState.PAUSE);
            if (ProgressDownSubscriber.mHttpProgressListenerList != null && !ProgressDownSubscriber.mHttpProgressListenerList.isEmpty()) {
                for (HttpProgressListener listener : ProgressDownSubscriber.mHttpProgressListenerList) {
                    listener.onPuase(info);
                }
            }
            AppInfoDao.getInstance().createOrUpdate((AppListInfo.ContentBean) info);
        }
        /*这里需要讲info信息写入到数据中，可自由扩展，用自己项目的数据库*/
    }


    /**
     * 删除所有正在下载的任务
     */
    public void deleteAllDown() {
        Iterator iter = downInfos.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Object val = entry.getValue();
            AppListInfo.ContentBean obj = (AppListInfo.ContentBean) val;
            ProgressDownSubscriber subscriber = subMap.get(obj.getAppId());
            subscriber.unsubscribe();//取消下载订阅
            obj.setCountLength(0);
            obj.setReadLength(0);
            obj.setState(DownState.STOP);
            obj.setUrl(null);
            obj.setSavePath(null);
            AppInfoDao.getInstance().createOrUpdate(obj);
        }
        subMap.clear();
        downInfos.clear();
    }

    /**
     * 暂停全部下载
     */
    public void pauseAll() {
        Iterator iter = downInfos.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Object key = entry.getKey();
            AppListInfo.ContentBean val = (AppListInfo.ContentBean) entry.getValue();
            ProgressDownSubscriber subscriber = subMap.get(val.getAppId());
            subscriber.unsubscribe();
            //如果应用正在下载，则将状态设置为暂停。
            if (val.getListener() != null) {
                val.getListener().onPuase();
            }
            AppInfoDao.getInstance().createOrUpdate(val);
        }
        subMap.clear();
        downInfos.clear();
    }


    public boolean isDownloading(DownInfo info) {
        return subMap.containsKey(((AppListInfo.ContentBean) info).getAppId());
    }


    /**
     * 获取正在下载的app
     *
     * @return
     */
    public AppListInfo.ContentBean getDownloadingInfo(DownInfo downInfo) {
        DownInfo info = downInfos.get(((AppListInfo.ContentBean) downInfo).getAppId());
        return (AppListInfo.ContentBean) info;
    }

    /**
     * 写入文件
     *
     * @param file
     * @param info
     * @throws IOException
     */
    private void writeCache(ResponseBody responseBody, File file, DownInfo info) throws IOException {
        if (!file.exists()) {
            /**
             * 创建应用市场需要的文件夹
             */
            FileUtil.mkdirs(configuration.DOWNLOAD_DIR);
            FileUtil.mkdirs(configuration.SILENCE_INSTALL_DOWNLOAD_DIR);
            FileUtil.mkdirs(configuration.TEMP);

            PkManager.createNewPlaintextFile(file);
        }

        long allLength = responseBody.contentLength();
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rwd");
        FileChannel channelOut = randomAccessFile.getChannel();
        try {
            MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, info.getReadLength(), allLength);
            byte[] buffer = new byte[1024 * 8];
            //读取长度
            int readLength;
            //读取位置
            int position = 0;
            while ((readLength = responseBody.byteStream().read(buffer)) != -1) {
                mappedBuffer.put(buffer, 0, readLength);
                position += readLength;
            }
        } catch (Exception e) {
            Log.e("yh------------", "writeCache错误:" + e.getMessage());
            responseBody.byteStream().close();
            channelOut.close();
            randomAccessFile.close();
            throw e;
        }
        responseBody.byteStream().close();
        channelOut.close();
        randomAccessFile.close();
    }
}
